package com.beiding.hotcode;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class HotClass implements MyClass {

    private JavaMonitor javaMonitor;

    HotClass(JavaMonitor javaMonitor) {
        this.javaMonitor = javaMonitor;

        //懒初始化
        // init();
    }

    // private boolean hasInit = false;

    JavaMonitor getJavaMonitor() {
        return javaMonitor;
    }

    private Map<String, Map<Integer, List<MethodHolder>>> methods = new HashMap<>();

    private void makeMethodParamMap() {

        methods.clear();

        //获取其中的静态方法
        Class<?> aClass = javaMonitor.getTheClass();

        for (Method method : aClass.getMethods()) {

            //只处理静态方法
            if (Modifier.isStatic(method.getModifiers())) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                Map<Integer, List<MethodHolder>> integerListMap = methods.computeIfAbsent(method.getName(), k -> new HashMap<>());
                List<MethodHolder> methodHolders = integerListMap.computeIfAbsent(parameterTypes.length, k -> new ArrayList<>());
                MethodHolder methodHolder = new MethodHolder(method, parameterTypes);
                methodHolders.add(methodHolder);
            }
        }

    }

    private MethodHolder getMethod(String method, Object... params) {
        Map<Integer, List<MethodHolder>> integerListMap = methods.get(method);
        if (integerListMap == null) {
            return null;
        }
        List<MethodHolder> methodHolders = integerListMap.get(params.length);
        if (methodHolders == null) {
            return null;
        }

        MethodHolder m = null;
        for (MethodHolder methodHolder : methodHolders) {
            if (methodHolder.match(params)) {
                if (m != null) {
                    throw new RuntimeException("无法确定方法:" + method + "(" + createParamTypesString(params) + ")");
                }
                m = methodHolder;
            }
        }
        if (m == null) {
            throw new RuntimeException("未找到方法:" + method + "(" + createParamTypesString(params) + ")");
        }
        return m;
    }

    @Override
    public Class getType() {
        checkChange();
        return javaMonitor.getTheClass();
    }

    public Object invokeCatch(String method, Object... params) throws Throwable {
        checkChange();

        try {
            MethodHolder m = getMethod(method, params);
            if (m == null) {
                throw new NoSuchMethodException(method + "(" + createParamTypesString(params) + ")");
            }
            return m.getMethod().invoke(null, params);
        } catch (InvocationTargetException e) {

            //抛出所有异常
            throw e.getTargetException();

        } catch (Exception e) {

            throw new RuntimeException(e);
        }
    }


    public void set(String field, Object value) {
        checkChange();
        try {
            Field f = javaMonitor.getTheClass().getField(field);
            f.set(null, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public Object get(String field) {
        checkChange();
        try {
            Field f = javaMonitor.getTheClass().getField(field);
            return f.get(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //不需要初始化
    /*private void init() {

        //先进行一次检查操作
        if (javaMonitor.checkChange()) {
            javaMonitor.reloadClass();
        }

        makeMethodParamMap();
    }
*/
    private void checkChange() {

        if (javaMonitor.checkChange()) {
            synchronized (javaMonitor) {
                if (javaMonitor.checkChange()) {
                    javaMonitor.reloadClass();

                    Class oClass = javaMonitor.getTheClass();

                    if (oClass == null) {
                        return;
                    }

                    //重载后的类
                    Class reloadClass = javaMonitor.getTheClass();
                    try {

                        Field[] declaredFields = reloadClass.getDeclaredFields();
                        for (Field declaredField : declaredFields) {
                            int modifiers = declaredField.getModifiers();
                            if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
                                declaredField.setAccessible(true);
                                try {
                                    Field field = oClass.getDeclaredField(declaredField.getName());
                                    if (Modifier.isStatic(field.getModifiers())) {
                                        field.setAccessible(true);
                                        Object v = field.get(null);
                                        if (v == null) {
                                            //有必要赋值即使是null
                                            declaredField.set(null, null);
                                        } else {
                                            if (declaredField.getType() == v.getClass()) {
                                                declaredField.set(null, v);
                                            }
                                        }
                                    }
                                } catch (NoSuchFieldException ignore) {

                                }
                            }
                        }

                        //如果父类相同,那么可以从父类开始递归复制内部属性
                        if (oClass.getSuperclass() == reloadClass.getSuperclass()) {
                            Class t = oClass.getSuperclass().getSuperclass();
                            if (t != null) {
                                while (t != Object.class && t != null) {
                                    Field[] fields = t.getDeclaredFields();
                                    if (fields != null) {
                                        for (Field field : fields) {
                                            if (Modifier.isStatic(field.getModifiers())) {
                                                field.setAccessible(true);
                                                field.set(null, field.get(null));
                                            }
                                        }
                                    }
                                    t = t.getSuperclass();
                                }
                            }

                        }
                        makeMethodParamMap();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }

        }

    }


    private String createParamTypesString(Object[] params) {

        StringBuilder builder = new StringBuilder();

        if (params.length > 0) {
            builder.append(params[0] == null ? "null" : params[0].getClass().getName());
            for (int i = 1; i < params.length; i++) {
                builder.append(",").append(params[i] == null ? "null" : params[i].getClass().getName());
            }
        }
        return builder.toString();
    }

    @Override
    public <T> MyMethod<T> method(String name, Class<T> type) {
        return params -> (T) HotClass.this.invoke(name, params);
    }


    public MyInstance newInstance(Object... params) {
        return new HotInstance(this, this.javaMonitor, params);
    }

}
